package com.bitantenna;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;

public class Channel {
	public Channel(PortAddress sourceAddress) {		
		setupUdpSockets(sourceAddress);
	}
	
	private DatagramSocket socket;
	/**longitud de un paquete de datos*/
	protected int datagramLength =10*4*8;
	private PortAddress sourceAddress;

	/** obtiene una cadena imprimible del identificador de mensaje, usado para mostrar paquete */
	public String messageStringFromPacket(byte[] packet) {
		byte m=Message.messageFromPacket(packet);
		if (m==Message.MessageIdKeepAlive) return "MessageKeepAlive"; 
		if (m==Message.MessageIdFrame) return "MessageSample";
		if (m==Message.MessageIdAttachConsumer) return "MessageAttachConsumerToRoot";
		return "Unknown";
	}

	public DatagramPacket receivePacket() throws IOException {
		return receivePacket(0);
	}

	/**
	 * 
	 * 
	 * @param timeOut tiempo de espera maximo para recibir un paquete o 0 si es infinito.
	 * @return el datagrama o null si se produjo un timeout
	 * @throws IOException
	 */
	public DatagramPacket receivePacket(int timeOut) throws IOException {
		if (sourceAddress.port()==-1) {
			System.err.println("warning: receivePacket ignored, channel has a source address without a port specified.");
			return null;
		} else {
			DatagramPacket p=new DatagramPacket(new byte[5000], 5000); //TODO - IMPORTANTE - tamano de datagrama hardcodeado y excedido, definir correctamente cual es el tamano.
			try {
				if (timeOut!=0) socket.setSoTimeout(timeOut);
				socket.receive(p);
				// Utils.printStackTrace();
			} catch(SocketTimeoutException ex) {
				return null;
			}
			return p;
		}
	}
	
	
	public void sendPacket(PortAddress target, Message message) {
		byte[] messageBytes=new byte[message.toByteSize()]; //TODO inneficiente como un carancho cruzando una pista de automovilismo.
		message.toBytes(messageBytes);
		sendPacket(target, messageBytes);
	}
	
	int jjj=0;
	private void sendPacket(PortAddress target, byte[] packet) {
		if (jjj>100 && (++jjj)%2==0/*Utils.testChance(0.01)*/) return; //Utils.sleepSimple(30000); // dropping probabilistico.
		
		if (sourceAddress.port()==-1) {
			System.err.println("warning: receivePacket ignored, channel has a source address without a port specified.");			
		} else {
			DatagramPacket p=new DatagramPacket(packet, packet.length,target.address(),target.port());
			/*if (packet.length==320) {
				throw new RuntimeException("INSPECCIONAR-TAMANO");
			}*/
			//System.err.println(this+",to:"+target.port()+" SS_SIZE: "+packet.length);
			//Utils.printStackTrace();
			try {
				socket.send(p);
			} catch (IOException e) { //TODO agregar manejo adecuado de error.
				throw new RuntimeException(e);
			}
		}
	}

	public PortAddress sourceAddress() {
		return sourceAddress;
	}

	public String toString() {
		return "SpotChannel sourceAddress:"+sourceAddress;
	}
	
	private void setupUdpSockets(PortAddress sourceAddress) {
		this.sourceAddress=sourceAddress;
		try {			
			if (sourceAddress.port()!=-1) {
				System.err.println("creating socket:"+sourceAddress.address()+","+sourceAddress.port());
				socket=new DatagramSocket(sourceAddress.port(),sourceAddress.address());							
			}
		} catch (SocketException e) { //TODO hacer manejo adecuado de error.
			throw new RuntimeException("cannot create socket with address: "+sourceAddress.address()+" port:"+sourceAddress.port(),e);
		}
	}
	
	protected void close() {		
		if (socket!=null && !socket.isClosed()) socket.close(); 
	}
}